// 根据规范只有两种原始类型可以用作对象属性键：
// 1. 字符串类型（使用其他类型会被自动转为字符串）
// 2. symbol 类型

// **********************************************************

// 【symbol】"symbol"值表示唯一的标识符。

// id 是描述（名称）为 'id' 的 symbol。
let id = Symbol('id')
let id2 = Symbol('id')

console.log(id === id2) // false 即使两个描述相同的 symbol 它们也不相等。

// **********************************************************

// 【symbol 不会被自动转换为字符串】

// 隐式转换会报错，显示转换 String() .toString() 不会报错。

let id3 = Symbol('id')

// alert(id3) // 报错
// alert(String(id3)) // 不报错 "Symbol(id)"
// alert(id3.toString()) // 不报错 "Symbol(id)"

// **********************************************************

// 【获取 symbol 描述】

let id4 = Symbol('id')

console.log(id4.description) // "id"

// **********************************************************

// 【作为对象的 “隐藏” 属性】

let user = {
	name: 'John',
	[Symbol('id')]: '123',
	[Symbol('id')]: '456',
}

let id5 = Symbol('id')

user[id5] = 'symbol键的属性值'

console.log(user[id5]) // "symbol键的属性值";

// Symbol 在 for...in 中会被忽略。
for (const porp in user) {
	console.log(porp) // name
}

// Object.keys 也会忽略 Symbol。
console.log(Object.keys(user)) // [ "name" ]

// Object.assign 会复制 symbol
let copyUser = Object.assign({}, user)
console.log(copyUser[id5]) // "symbol键的属性值"

// **********************************************************

// 【全局 symbol】

// 使用 Symbol.for(key) 进行创建，它会先在 注册表 中读取，如果不存在则创建一个，如果存在则返回该 symbol。

let id6 = Symbol.for('id')
let id6Again = Symbol.for('id')

console.log(id6 === id6Again) // true

let obj = {
	[id6]: '123',
	[id6Again]: '456',
}

// 因为是相同的 symbol 所以下面的键把上面的键给覆盖了， id6 和 id6Again 是同一个键。

console.log(obj[id6]) // "456"
console.log(obj[id6Again]) // "456"

// **********************************************************

// 【symbol.keyFor】通过 key 返回的全局 symbol 的描述（名称），非全局 symbol 返回 undefined。

let id7 = Symbol('name') // 常规 Symbol
let id8 = Symbol.for('name') // 全局 Symbol

let sym = Symbol.keyFor(id7)
let sym2 = Symbol.keyFor(id8)

console.log(sym) // undefined，非全局
console.log(sym2) // "name"，全局Symbol

// 但是所有 Symbol 都有描述（名称）
console.log(id7.description) // "name"
console.log(id8.description) // "name"

// **********************************************************

// 总结：
// symbol 总是不同的值，即使它们有相同的名字。如果我们希望同名的 symbol 相等，那么我们应该使用全局注册表：Symbol.for(key)，存在则返回该symbol，不存在则新创建。

// symbol应用场景：
// 1. 为对象添加“隐藏”属性。
// 2. 系统 symbol 通过 Symbol.* 来改变一些应用场景。

// 获取 symbol 的方式：
// 内建方法 Object.getOwnPropertySymbols(obj) 获取所有的 symbol。
// Reflect.ownKeys(obj) 的方法可以返回一个对象的所有 键，包括symbol。
